AWS CDKでCloudFront+S3 Webサイトをホスティングする
ojosamaのインフラ移行でやった作業の一つということで忘れないようにメモ 本題
作成するリソース
ユーザからアクセスさせるなら固定のドメインの発行も必要だろう、と
別になくてもCloudFrontがIDドメイン(なんとかかんとか.cloudfront.netってやつ)を発行してくれるが、このドメインに直接アクセスさせるとディストリビューションを作り直したときにドメインが変わってしまう
事前にホストゾーンを作成しておく必要あり
CDKのコードとしてはこうなった
code:aws-cdk-stack.ts
import * as cdk from "aws-cdk-lib"
import { Construct } from "constructs"
// 必要に応じて修正する
const hostedZoneName = "ホストゾーン名"
function toUpper(name: string): string {
return name0.toUpperCase() + name.slice(1) }
function createStaticHostingSite(
scope: Construct,
name: string,
hostedZone: cdk.aws_route53.IHostedZone
) {
const idSuffix = toUpper(name)
const domainName = ${name}.${hostedZoneName}
const cert = new cdk.aws_certificatemanager.DnsValidatedCertificate(
scope,
CertificateManagerCertificate${idSuffix},
{
domainName: domainName,
hostedZone: hostedZone,
// CloudFrontの設定はバージニア北部固定なのでリージョン指定をする
region: "us-east-1",
validation: cdk.aws_certificatemanager.CertificateValidation.fromDns(),
}
)
// サブドメイン名と同じ名前のバケットを作成
const s3Bucket = new cdk.aws_s3.Bucket(scope, S3Bucket${idSuffix}, {
bucketName: domainName,
// ブロックパブリックアクセスをすべてON
blockPublicAccess: cdk.aws_s3.BlockPublicAccess.BLOCK_ALL,
// 消しても良いので設定
removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true,
})
const cloudFrontOAI = new cdk.aws_cloudfront.OriginAccessIdentity(
scope,
CloudFrontOriginAccessIdentity${idSuffix},
{
comment: "Unique Domain Hosting Environment",
}
)
const cloudFrontDistribution = new cdk.aws_cloudfront.Distribution(
scope,
CloudFrontDistribution${idSuffix},
{
// キャッシュビヘイビア設定
defaultBehavior: {
// オリジンID指定
origin: new cdk.aws_cloudfront_origins.S3Origin(s3Bucket, {
originAccessIdentity: cloudFrontOAI,
}),
// 自動圧縮有無指定
compress: true,
// HTTPメソッド指定
allowedMethods: cdk.aws_cloudfront.AllowedMethods.ALLOW_GET_HEAD,
cachedMethods: cdk.aws_cloudfront.CachedMethods.CACHE_GET_HEAD,
// ビューアプロトコルポリシー指定
viewerProtocolPolicy:
cdk.aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
// キャッシュポリシー指定 (CachingOptimized)
cachePolicy: cdk.aws_cloudfront.CachePolicy.CACHING_OPTIMIZED,
},
// SPA対応設定
errorResponses: [
{
httpStatus: 403,
responsePagePath: "/index.html",
responseHttpStatus: 200,
ttl: cdk.Duration.minutes(0),
},
{
httpStatus: 404,
responsePagePath: "/index.html",
responseHttpStatus: 200,
ttl: cdk.Duration.minutes(0),
},
],
// 料金クラス指定
// 多少なりとも安くするためにアジアが含まれる 200 を指定
priceClass: cdk.aws_cloudfront.PriceClass.PRICE_CLASS_200,
// ディストリビューション有効無効指定
enabled: true,
// SSL証明書設定
certificate: cert,
// セキュリティポリシー指定
minimumProtocolVersion:
cdk.aws_cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021,
// HTTPバージョン指定
httpVersion: cdk.aws_cloudfront.HttpVersion.HTTP2,
// ルートURL指定
defaultRootObject: "index.html",
// IPv6有無指定
enableIpv6: true,
}
)
// CloudFrontに対して分かりやすいユーザ向けのサブドメインを発行
new cdk.aws_route53.CnameRecord(
scope,
route53CloudFrontCnameRecord${idSuffix},
{
zone: hostedZone,
recordName: domainName,
domainName: cloudFrontDistribution.domainName,
ttl: cdk.Duration.seconds(600),
}
)
}
export class AWSCDKStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props)
// The code that defines your stack goes here
const hostedZone = cdk.aws_route53.HostedZone.fromLookup(
scope,
route53HostedZone,
{
domainName: hostedZoneName,
}
)
createStaticHostingSite(this, "アプリケーション名", hostedZone)
// example resource
// const queue = new sqs.Queue(this, 'AwscdkQueue', {
// visibilityTimeout: cdk.Duration.seconds(300)
// });
}
}
注意事項
画面に再アクセスしてもファイルが更新されていない、といった事態になる
デプロイ処理に invalidation を入れるの忘れてて画面が更新されなくて気づいた
感想
静的ファイルのホスティングだけならたったこんだけで実現できて素晴らしい
無料枠に大体おさまるのでほとんどお金かからないし
参考